#include "chartdiagram.hh"
#include "node.hh"
#include <stdio.h>
#include <QCursor>
#include <QGraphicsSceneMouseEvent>
#include <math.h>

dsa::ChartDiagram::ChartDiagram(QWidget *parent)
  : QGraphicsView (parent)
  , _speed (10.0f)
  , _firstPie (0)
  , _lastPie (0)
{
  _scene = new QGraphicsScene ();
  _scene->clear();

  _center = new dsa::ChartCenter (75.0f);
  _center->UpdateCenter();
  _scene->addItem(_center);

  _scene->setSceneRect(-250, -250, 500, 500);
  setScene(_scene);


  _timer.setInterval(10);
  _isUp = false;
  SetInUpdate(false);

  connect (_center, SIGNAL(Clicked()), this, SLOT(CenterClicker()));
}

void dsa::ChartDiagram::Set(dsa::Node *node, QList<dsa::Node *> nodes)
{
  _node = node;
  _nodes = nodes;
  BuildDown ();
}


void dsa::ChartDiagram::BuildDown()
{
  if (_isUp)
    {
      disconnect (&_timer, SIGNAL(timeout()), this, SLOT(UpdateBuildUp()));
      connect (&_timer, SIGNAL(timeout()), this, SLOT(UpdateBuildDown()));
      SetInUpdate(true);
      _timer.start();
    }
  else
    {
      UpdateChartsAndBuildUp ();
    }
}

void dsa::ChartDiagram::BuildUp()
{
  if (!_isUp)
    {
      connect (&_timer, SIGNAL(timeout()), this, SLOT(UpdateBuildUp()));
      disconnect (&_timer, SIGNAL(timeout()), this, SLOT(UpdateBuildDown()));
      SetInUpdate(false);
      _timer.start();
    }
}

void dsa::ChartDiagram::UpdateChartsAndBuildUp()
{
  for (dsa::ChartPie* pie = _firstPie;
       pie;
       )
    {
      _scene->removeItem(pie);

      dsa::ChartPie* nextPie = pie->GetNext();
      delete pie;
      pie = nextPie;
    }

  _scene->removeItem(_center);

  qint64 totalSize = _node->GetSize();
  dsa::ChartPie* prev = 0;

  printf ("Update Chart diagram\n");
  foreach (dsa::Node* node, _nodes)
    {
      printf ("  %llu => '%s'\n", node->GetSize(), (const char*)node->GetName().toAscii());
      dsa::ChartPie* pie = new dsa::ChartPie (200.0f, prev, (double)node->GetSize() * 360.0f / (double)totalSize);
      pie->SetNode(node);
      if (!prev)
        {
          _firstPie = pie;
        }
      connect (pie, SIGNAL(Clicked()), this, SLOT(PieClicked()));

      prev = pie;
      _lastPie = pie;
      _scene->addItem(pie);
    }
  fflush(stdout);
  _scene->addItem(_center);

  BuildUp();

}

void dsa::ChartDiagram::CenterClicker()
{
   emit ChangeDirectory(_node->GetParent());
}

void dsa::ChartDiagram::PieClicked()
{
  dsa::ChartPie* pie = static_cast<dsa::ChartPie*>(sender ());
  emit ChangeDirectory(pie->GetNode());
}

void dsa::ChartDiagram::SetInUpdate (bool inUpdate)
{
  for (dsa::ChartPie* pie = _firstPie; pie; pie = pie->GetNext())
    {
      pie->SetInUpdate(inUpdate);
    }
}

void dsa::ChartDiagram::UpdateBuildUp()
{
  dsa::ChartPie* pie = _firstPie;
  float startAngle = 90;
  float updateNext = true;

  for (dsa::ChartPie* pie = _firstPie; pie; pie = pie->GetNext())
    {
      float angle = pie->GetAngle();
      if (angle < pie->GetRealAngel() && updateNext)
        {
          angle += _speed;
          if (angle >= pie->GetRealAngel())
            {
              angle = pie->GetRealAngel();
            }
          pie->SetAngle(startAngle, angle);
          startAngle += angle;
          updateNext = false;
        }
      else
        {
          pie->SetAngle(startAngle, angle);
          startAngle += angle;
        }
      pie->UpdatePie();
    }

  if (updateNext)
    {
      SetInUpdate(false);
      _isUp = true;

      _timer.stop();
    }
}

void dsa::ChartDiagram::UpdateBuildDown()
{
  float updateNext = true;

  for (dsa::ChartPie* pie = _lastPie; pie; pie = pie->GetPrev())
    {
      float angle = pie->GetAngle();
      if (angle > 0.0f && updateNext)
        {
          angle -= _speed;
          if (angle <= 0.0f)
            {
              angle = 0.0f;
            }
          pie->SetAngle(pie->GetStartAngle(), angle);
          updateNext = false;
          pie->UpdatePie();
        }
    }

  if (updateNext)
    {
      SetInUpdate(false);
      _isUp = false;
      _timer.stop();

      UpdateChartsAndBuildUp ();
    }
}


dsa::ChartPie::ChartPie (float radius, dsa::ChartPie *prevSibling, float angle)
  : QGraphicsEllipseItem(0)
  , _radius (radius)
  , _angle (0)
  , _startAngle(0)
  , _realRadius(radius)
  , _realAngle(angle)
  , _radiusSpeed (2.5f)
  , _prevSibling (prevSibling)
  , _nextSibling (0)
  , _node (0)
  , _active (false)
{
  setAcceptHoverEvents(true);
  if (prevSibling)
    {
      prevSibling->_nextSibling = this;
    }

  QPen pen (QColor (255, 255, 255));
  pen.setWidth(2);
  setPen(pen);

  connect (&_timer, SIGNAL(timeout()), this, SLOT(Timeout()));
  _timer.setInterval(10);
  _timer.stop();
}



void dsa::ChartPie::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
  if (!_node || _node->GetType() != dsa::Node::Type_Dir)
    {
      return;
    }

  grabMouse();
  _active = true;
}

void dsa::ChartPie::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
  if (!_node || _node->GetType() != dsa::Node::Type_Dir)
    {
      return;
    }
  ungrabMouse();
  if (_active && contains(event->pos()))
    {
      emit Clicked ();
    }
  _active = false;
}

void dsa::ChartPie::SetNode(dsa::Node *node)
{
  if (_node)
    {
      disconnect (_node, SIGNAL(HoverChanged(bool)), this, SLOT(EnableHover(bool)));
    }
  _node = node;
  if (_node)
    {
      setBrush(_node->GetColor());
      connect (_node, SIGNAL(HoverChanged(bool)), this, SLOT(EnableHover(bool)));
    }
}

dsa::Node *dsa::ChartPie::GetNode() const
{
  return _node;
}

void dsa::ChartPie::Timeout()
{
  bool needRadius = true;
  if (_radius < _destRadius)
    {
      _radius += _radiusSpeed;
      if (_radius >= _destRadius)
        {
          _radius = _destRadius;
          needRadius = false;
        }
    }
  else if (_radius > _destRadius)
    {
      _radius -= _radiusSpeed;
      if (_radius <= _destRadius)
        {
          _radius = _destRadius;
          needRadius = false;
        }

    }



  bool needUpdate = needRadius;
  if (!needUpdate)
    {
      _timer.stop();
    }

  UpdatePie();

}

void dsa::ChartPie::SetAngle(float start, float angle)
{
  _startAngle = start;
  _angle = angle;
}

float dsa::ChartPie::GetAngle() const
{
  return _angle;
}

float dsa::ChartPie::GetRealAngel() const
{
  return _realAngle;
}

float dsa::ChartPie::GetStartAngle() const
{
  return _startAngle;
}

void dsa::ChartPie::SetInUpdate(bool inUpdate)
{
  _inUpdate = inUpdate;
}

dsa::ChartPie* dsa::ChartPie::GetNext() const
{
  return _nextSibling;
}

dsa::ChartPie* dsa::ChartPie::GetPrev() const
{
  return _prevSibling;
}

void dsa::ChartPie::UpdatePie()
{
  setRect(-_radius, -_radius, _radius * 2.0f, _radius * 2.0f);
  setStartAngle((int)(_startAngle * 16.0f));
  setSpanAngle((int)(_angle * 16.0f));
}

void dsa::ChartPie::EnableHover(bool enable)
{
  if (enable)
    {
      _destRadius = _realRadius * 1.2f;
      _timer.start();
    }
  else
    {
      _destRadius = _realRadius;
      _timer.start();

    }
}


void dsa::ChartPie::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
  if (_inUpdate)
    {
      return;
    }

  _node->ChangeHover(true);

  if (_node && _node->GetType() == dsa::Node::Type_Dir)
    {
      setCursor(QCursor(Qt::PointingHandCursor));
    }
}

void dsa::ChartPie::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
{
  if (_inUpdate)
    {
      return;
    }

  _node->ChangeHover(false);
  setCursor(QCursor(Qt::ArrowCursor));
}


dsa::ChartCenter::ChartCenter (float radius)
  : QGraphicsEllipseItem (0)
  , _realRadius (radius)
  , _radius (radius)
  , _active (false)
{
  setAcceptHoverEvents(true);
  setBrush(QColor(0, 0, 128));

  QPen pen (QColor(255, 255, 255));
  pen.setWidth(4);
  setPen(pen);
}

void dsa::ChartCenter::UpdateCenter()
{
  setRect(-_radius, -_radius, _radius * 2.0f, _radius * 2.0f);
}

void dsa::ChartCenter::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
  grabMouse();
  _active = true;
}

void dsa::ChartCenter::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
  ungrabMouse();
  if (_active && contains(event->pos()))
    {
      emit Clicked ();
    }
  _active = false;
}

void dsa::ChartCenter::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
  _radius = _realRadius * 1.2f;
  UpdateCenter();
  setCursor(QCursor(Qt::PointingHandCursor));
}

void dsa::ChartCenter::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
{
  _radius = _realRadius;
  UpdateCenter();
  setCursor(QCursor(Qt::ArrowCursor));
}

